썸네일: Unsplash의Paul Hanaoka
출처 : 웹 성능 최적화 기법
웹 성능 최적화 기법(루비페이퍼 사) 도서에 대한 핵심 내용과 지식을 정리한 포스트입니다. 포스트에 올라오는 내용은 도서의 일부이기 때문에 더 자세한 내용이 궁금하신 분들은 출처에서 도서를 구매해 읽어보시는 것을 추천드립니다.
6.1 캐시
- 캐시는 요청에 빠르게 응답하기 위해 서버와 클라이언트 사이에서 응답 콘텐츠의 사본을 저장하는 공간을 의미한다. 또한 캐시를 유지하고 처리해주는 별도 서버를 캐시 서버라고 한다.
- 웹 아키텍처에는 다양한 캐시 서버가 존재한다.
캐시종류 | 위치 | 이점 |
---|---|---|
브라우저 캐시 | 브라우저 | 한 번 다운로드한 리소스들을 재사용하여 사이트 로딩을 빠르게 함. |
프록시 캐시 | 브라우저와 ISP | 조직 내 사용자들이 접속하는 웹 사이트의 리소스들을 캐시하여 네트워크 연결과 대역폭 사용률을 감소시키고 사용자의 웹 사이트 로딩을 빠르게 함 |
트랜스패어런트 캐시 | ISP | ISP는 이 캐시를 사용하여 ISP 간 대역폭이 낭비되는 것을 막는다. |
리버스 프록시 캐시 | ISP와 웹 서버 | 원본 서버로 향하는 트래픽 대역폭을 감소시키고, 사용자 응답을 개선한다. |
- 캐시 서버는 원본 서버뿐 아니라 프록시 서버, ISP 라우터 등 다양한 위치에 존재하며 네트워크 대역폭 및 비용 절감을 위해 사용된다.
- 대표적인 웹서버인 Apache HTTP Server에서도 moc_cache, mod_cache_disk, mod_file_cache 모듈들을 활성하해 캐시 서버로 활용할 수 있고, Nginx에서도 콘텐츠 캐시 기능을 제공한다.
- Apache Traffic Server : 아파치 소프트웨어 재단에서 재공하는 오픈 소스 웹 캐시 서버
- Nginx : 대중적인 웹 서버 중 하나로 콘텐츠 캐시 기능 제공
- Varnish Cache : 캐시를 통한 HTTP 가속을 목적으로 개발된 오픈 소스 소프트웨어
6.2 웹 캐시 동작 원리
- 웹 캐시는 웹 서버와 웹 브라우저 중간에 존재하면서 최초 원본 콘텐츠 요청을 최종 서버에 보내 응답을 받은 후 그 복사본을 만들어 저장하고 사용자에게 응답한다.
- 이후 같은 콘텐츠에 대한 요청이 오면 최종 서버에서 원본 서버를 가져오는 대신 복사본을 사용자에게 전달한다.
- 모든 웹 콘텐츠를 캐시할 수 없기 때문에 캐시 컨트롤 과정을 필요로 한다.
6.2.1 HTTP
- 인터넷에서 데이터를 주고받기 위한 클라이언트/서버 모델을 따르는 프로토콜
- OSI 7 계층 모델의 마지막 7계층인 Application 레벨의 프로토콜이며 TCP/IP 위에서 동작한다.
- 클라이언트가 특정 URL에 대한 요청(Request)을 서버 측에 보내면 서버는 요청을 처리해 URL에 해당하는 응답(response) 클라이언트에게 되돌려 보낸다.
- 일반적으로는 HTTP를 요청하는 브라우저와 콘텐츠를 제공하는 웹 서버를 클라이언트/서버로 인식할 수 있지만, 캐시 서버와 같은 프록시 서버가 중간에 제공되면 브라우저를 클라이언트 측, 웹 서버를 서버측으로 여길수 있다.
HTTP 특징
- 클라이언트 서버 모델로 동작
- 비연결성이며 상태를 유지하지 않는 프로토콜이다
- 클라이언트와 서버 간 HTTP 메시지를 주고받으며 통신한다.
- HTTP 메시지는 헤더와 바디 부분으로 구분되어 전송된다
- 하위 Transport Layer 프로토콜로 TCP를 사용하며, 일반적으로 80포트를 사용한다.
6.2.2 HTTP 캐시 제어 방식
- HTTP/1.0까지는 캐시를 제어하는 명시적인 기술이 없었으며, 원본 서버 자원들이 언제까지 유효한지, 만료일 이후 해당 자원이 실제 변경되었는지 확인하는 요청/응답 헤더를 정의
- HTTP/1.1에서 Cache-Control 헤더가 추가되어 다음과 같은 목적을 정의
- 원본 서버로의 요청수 최소화
- 완전한 콘텐츠를 응답할 필요 없음
Expire
- HTTP/1.0 은 Expire 헤더를 사용해 원본 서버 콘텐츠의 유효 기간을 지정하도록 정의
- Expire, Date 헤더를 함께 보내야 하며 Date 헤더는 요청에 대한 응답이 작성된 시점을 표시한다.
Time To Live(TTL) = Expire_value - Date_value
- 정확한 시간 제공을 위해 서버는 글로벌 표준 시간과 동기화되어야 하며 **Network Time Protocol(NTP)**의 메커니즘을 사용해 서버 간 또는 클라이언트-서버 간 동기화를 확인해야 한다.
Cache-Control:max-age
- HTTP/1.1 에서는 Cache-Control:max-age라는 헤더로 콘텐츠의 캐시 유지 시간을 정의
- 캐시에 특정 콘텐츠를 얼마나 오래 유지하고 있어야 하는지 명시적으로 설정하며, 기간이 지나면 캐시 서버는 원본 서버에 해당 콘텐츠 변경 여부를 체크하거나 새로 갱신해야함
- Expire와 차이는 Expire는 만료 일자를 지정하고, Cache-Control:max-age는 유효 기간을 지정한다.
- 두 헤더가 모두 명시된 경우 Cache-Control을 우선 사용
- 캐시 서버에서 Cache-Control 헤더를 사용한 동작 순서
- 첫 번째 요청에 대해 캐시된 응답이 없으므로, 원본 서버에 그대로 요청 전송
- 첫 번째 요청에 대해 원본 서버에서 응답을 생성하고 객체가 언제까지 유효한지 HTTP 헤더에 명시
- 캐시 서버는 해당 요청에 대한 캐시 키와 응답을 저장하고 만료일 설정 후, 요청자에게 응답 전송
- 이후 같은 요청을 한 시점이 만료일을 지나지 않았으면 캐시에 저장된 응답을 사용자에게 전송
Cache-Control:s-maxage
- CDN과 같은 공용 캐시 주기를 관리한다.
- 대부분 CDN은 동시에 사용하면 통합 관리하기가 쉽지 않아 s-maxage를 이용하면 사용중인 모든 CDN의 캐시 주기를 일괄적으로 설정하거나 변경할 수 있다.
ETag(Entity Tag)
- 원본 서버가 리소스를 식별하기 위해 부여하는 고유 번호
- 캐시 서버에서는 ETag를 사용해 원본 서버의 리소스가 시간이 지나 만료되었는지 캐시된 리소스를 새로 갱신해야 하는지 여부를 판단할 수 있다.
- 따옴표 안 임의 문자들의 조합으로 구성하며 어떤 조합이 될지는 전적으로 ETag를 생성하는 원본 서버가 결정한다.
- Strong ETag, Weak ETag 두 가지로 구분한다.
- StrongETag는 모든 리소스에 대해 유일한 값을 가진다(생성 어려움)
- WeakETag는 간단하게 생성할 수 있지만 신뢰도가 약해진다.
Cache-Control:public
- public 설정시 응답은 모든 캐시 서버에 캐시될 수 있고 사용자 제한 없이 모든 사용자에게 응답이 전달될 수 있다.
Cache-Control:private
- private 설정시, HTTP 요청에 대한 응답은 요청한 사용자만 캐시할 수 있고 CDN 같은 범용 캐시 서버에는 캐시할 수 없다.
Cache-Control:no-cache
- 요청 헤더에서 사용되는 경우
- 브라우저가 원본 서버나 중간에 존재하는 캐시 서버들에게 캐시된 응답을 받지 않겠다는 메시지를 전달하는 것과 같다.
- max-age = 0을 사용하는 것과 비슷하지만 no-cache의 경우 캐시 서버는 항상 원본 서버로부터 최신 응답을 받는데 반해, max-age = 0은 콘텐츠 변경 여부를 먼저 확인하고 유효하면 캐시 값을 반환한다.
- 응답 헤더에서 사용되는 경우
- 원본 서버가 캐시 서버들에게 캐시된 응답을 보내기 전 원본 서버를 항상 확인하도록 강제한다
Cache-Control:no-store
- no-store는 서버가 로컬 저장소에 메시지를 저장하지 않도록 지시한다.
6.2.3 캐시 유효성 체크
- 사용자가 특정 웹 콘텐츠에 대한 요청을 캐시로 보냈을 때 캐시에 저장되어 있는 응답의 age가 max-age 값을 넘었다면 응답은 더 이상 신뢰할 수 없으므로 캐시는 원본 서버에 요청을 보내 새 응답을 받아야 한다.
- 이때 원본 서버에 있는 웹 콘텐츠에 어떠한 변경이 있었다면 원본 서버는 당연히 새로운 응답을 만들어 캐시에 되돌려주고 max-age 값을 다시 설정해 캐시에 저장하도록 지시해야 한다.
- 그러나 콘텐츠의 변경이 없는 상황에선 이런 방식은 서버 자원과 네트워크 대역폭을 낭비하므로 HTTP 표준에서는 조건부 요청 메커니즘을 제공함
- 조건부 요청 메커니즘은 저장된 응답 TTL이 만료되었을 경우 캐시가 항상 원본 서버에서 완전한 콘텐츠를 받아오는 대신 TTL 주기 동안 콘텐츠에 변화가 있을 때에만 새 응답을 만들도록 요청한다.
시간 기반의 조건부 요청
- 어떤 요청에 대한 원본 서버의 콘텐츠가 캐시에 저장된 후 변경되었는지 여부를 콘텐츠 최종 변경 시간 중심으로 확인하는 방법
- 최종 변경된 날짜와 시간을 저장해서 Last-Modified 헤더에 담아 전송하여 최종 변경 시간 이후 변경 사항이 있다면 전체 응답을 다시 하도록 요청하는 방식
콘텐츠 기반의 조건부 요청
- 어떤 요청에 대한 원본 서버의 콘텐츠가 캐시에 저장된 후 변경되었는지 여부를 콘텐츠 고윳값 중심으로 확인하는 방법
- 주로 해시값으로 고윳값을 추출하며, 원본 서버는 ETag헤더에 고윳값을 담아 보낸다.
- 일치하는 고윳값이 없다면 전체 응답을 주도록 요청한다.
6.2.4 캐시 콘텐츠 갱신
- 콘텐츠가 변경되면 캐시에 저장된 복사본들을 갱신할 필요성이 있다.
- 갱신의 2가지 방법
- 퍼지
- 무효화
퍼지(purge)
- 저장소를 완전히 지우는 방식으로, 대부분 캐시 서버가 캐시를 모두 지우는 명령어, API를 제공
- 프로모션을 하거나 새로운 페이지 오픈 시 갑작스러운 퍼지는 피하고 테스트 툴을 이용해 리소스들을 캐시에 저장해두는 것이 권장됨
무효화(invalidate)
- 조건부 요청을 통해 캐시된 리소스들 중 변경이 있는 리소스들만 새로 갱신하는 방법
- Cache-Control 헤더를 사용해 캐시 서버 내용을 강제로 무효화할 수 있다.